cmake_minimum_required(VERSION 3.16)
project(erlua)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_VERBOSE_MAKEFILE ON)      # 查看实际build的时候执行的命令
option(USE_GCOV "Create a GCov-enabled build." OFF)

message("================开始查找Erlang库目录================")
# cmake 的 find_package 有两个模式:MODULE模式 和 CONFIG 模式
# 新代码可以都使用 CONFIG 模式
# 在以下三种情况下,find_package工作在CONFIG模式:
#   1.指定了 CONFIG 关键字
#   2.指定了 NO_MODULE 关键字
#   3.使用了 MODULE 不支持的选项
# CONFIG模式,在 CMAKE_PREFIX_PATH 或 <PackageName>_ROOT中寻找 <PackageName>Config.cmake
# 如果定义了<PackageName>_DIR cmake变量，那么<PackageName>_ROOT 不起作用
# sudo apt install erlang erlang-dev
set(Erlang_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Erlang REQUIRED NO_MODULE)
message(Erlang_INCLUDE_DIRS:${Erlang_INCLUDE_DIRS})
message(Erlang_LIBRARIES:${Erlang_LIBRARIES})

message("================开始查找Lua库目录================")
# sudo apt install liblua5.3-dev
find_package(Lua 5.3 REQUIRED)
message(LUA_LIBRARIES:${LUA_LIBRARIES})
message(LUA_INCLUDE_DIR:${LUA_INCLUDE_DIR})

message("================开始识别nif代码================")
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")      # 给C1编译器设置编译字符集!
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
aux_source_directory(c_src/ LIB_SRCS)
include_directories(c_include/)
set(LIB_NAME erlua)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) # 这是对所有目标打开pic选项
add_library(${LIB_NAME} SHARED ${LIB_SRCS})
if(${USE_GCOV})
    target_compile_options(${LIB_NAME} PUBLIC "--coverage")
    target_compile_options(${LIB_NAME} PUBLIC "-O0")
    target_compile_options(${LIB_NAME} PUBLIC "-g3")
    target_link_libraries(${LIB_NAME} PRIVATE
            ${Erlang_LIBRARIES}
            ${LUA_LIBRARIES}
            # pthread
            gcov
            )
else()
    # target_compile_options(${LIB_NAME} PUBLIC "-O1")
    target_link_libraries(${LIB_NAME} PRIVATE
            ${Erlang_LIBRARIES}
            ${LUA_LIBRARIES}
            # pthread
            )
endif()
target_include_directories(${LIB_NAME} PRIVATE
        ${Erlang_INCLUDE_DIRS}
        ${LUA_INCLUDE_DIR}
        # pthread
        )
# 将动态库文件名中的前缀 lib替换成空字符串(erlang nif的要求)
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")

# 注意,加了 PRE_BUILD 的 add_custom_command 命令 是另一种命令了!
# 这里并不是添加编译目标,而是给编译目标添加 编译前要执行的命令
# *.gcno 文件在编译的时候会重新生成,所以我们只需要删除gcda文件即可
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
    add_custom_command(TARGET ${LIB_NAME} PRE_BUILD
            COMMAND find . -name *.gcda -exec rm {} \;
            VERBATIM
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            )
else()
    add_custom_command(TARGET ${LIB_NAME} PRE_BUILD
            COMMAND ${CMAKE_COMMAND} -E rm -f *.gcda # 只支持3.17以上的版本,ubuntu默认版本是3.16
            VERBATIM
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            )
endif()
message(LIB_SRCS:${LIB_SRCS})
message(LIB_NAME:${LIB_NAME})

# add_custom_command 和 add_custom_target的区别:
# add_custom_command 编译目标，生成输出文件(如:xxx.beam)
# add_custom_target 的执行没有输出文件,必然执行
# 编译erlang代码
message("================开始识别erl代码================")
file(GLOB_RECURSE ERL_SOURCES e_src/**.erl)
message(ERL_SOURCES:${ERL_SOURCES})
set(BEAM_LS "")
message(CMAKE_CURRENT_BINARY_DIR:${CMAKE_CURRENT_BINARY_DIR})
message(CMAKE_BINARY_DIR:${CMAKE_BINARY_DIR})
foreach(E_SOURCE ${ERL_SOURCES})
    # string(REGEX REPLACE ".*/" "" NAME_WITH_NO_PATH ${E_SOURCE})
    # string(REGEX REPLACE "\\.erl" ".beam" TARGET_NAME ${NAME_WITH_NO_PATH})
    get_filename_component(NAME_WITHOUT_WLE ${E_SOURCE} NAME_WLE)
    set(TARGET_NAME ${NAME_WITHOUT_WLE}.beam)
    add_custom_command(OUTPUT ${TARGET_NAME}
            COMMAND erlc ARGS -o ${CMAKE_CURRENT_BINARY_DIR} ${E_SOURCE}
            DEPENDS ${E_SOURCE} # 加了这一行，erl改过以后，直接build（而不需要rebuild）就能构建这一目标
            )
    list(APPEND BEAM_LS ${TARGET_NAME})
endforeach()
message(BEAM_LS:${BEAM_LS})
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${BEAM_LS})
# 和上面命令作用一样,但是居然 ${BEAM_LS} 是一个列表的时候,居然会报错....
#set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${BEAM_LS})

## 下面一行add_custom_target作用和Makefile中添加以下两行相同,确保上面添加的目标会被生成
## all: run
## run: ${BEAM_LS}
#add_custom_target(run ALL DEPENDS ${BEAM_LS})

message("================添加“执行测试的目标”================")
file(GLOB_RECURSE LUA_SOURCES luascript/*.lua)
set(LUA_FILES_LS "")
foreach(L_SOURCE ${LUA_SOURCES})
    get_filename_component(NAME_WITHOUT_PATH ${L_SOURCE} NAME)
    add_custom_command(OUTPUT ${NAME_WITHOUT_PATH}
            COMMAND ${CMAKE_COMMAND} -E copy ${L_SOURCE} ${CMAKE_BINARY_DIR}/${NAME_WITHOUT_PATH}
            DEPENDS ${L_SOURCE}
            )
    list(APPEND LUA_FILES_LS ${NAME_WITHOUT_PATH})
endforeach()
# 只有erlua.so或 任意一个*.beam重新生成的时候，才重新产生check_sum.txt。
# 这样才能达成在有改变的情况下，build all才会执行测试
set(CHECK_SUM_TXT check_sum.txt)
add_custom_command(
        OUTPUT ${CHECK_SUM_TXT}
        COMMAND ctest -C Debug -VV --output-on-failure
        COMMAND ${CMAKE_COMMAND} -E sha256sum $<TARGET_FILE_NAME:${LIB_NAME}> ${BEAM_LS} ${LUA_FILES_LS} > ${CHECK_SUM_TXT}
        DEPENDS ${LIB_NAME} ${BEAM_LS} ${LUA_FILES_LS}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        VERBATIM
        )
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/${CHECK_SUM_TXT})
message("================覆盖度报告目标================")
if(${USE_GCOV})
    set(REPORT_FILE coverage/coverage.info)
    add_custom_command(OUTPUT ${REPORT_FILE}
            COMMAND mkdir -p coverage
            COMMAND lcov --base-directory . --directory . -c -o ${REPORT_FILE}
            COMMAND lcov --remove ${REPORT_FILE} "/usr*" -o ${REPORT_FILE}   # 移除 以/usr开头的库文件的覆盖度
            COMMAND genhtml coverage/coverage.info --output-directory coverage
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            DEPENDS ${CHECK_SUM_TXT}
            VERBATIM
            )
    set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/coverage)
    add_custom_target(report_target ALL DEPENDS ${REPORT_FILE})
else()
    add_custom_target(check_sum_target ALL DEPENDS ${CHECK_SUM_TXT})
endif()

message("================开始识别测试用例================")
#set(VALGRIND_PREFIX valgrind --tool=memcheck --leak-check=full --track-origins=yes --leak-resolution=high --show-reachable=yes --log-file=memchecklog.log)
set(VALGRIND_PREFIX "")
enable_testing()
add_test(NAME case1
        COMMAND ${VALGRIND_PREFIX} erl -noshell -s run case1 -s init stop
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        )
add_test(NAME case2
        COMMAND ${VALGRIND_PREFIX} erl -noshell -s run case2 -s init stop
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        )
add_test(NAME case3
        COMMAND  ${VALGRIND_PREFIX} erl -noshell -s run case3 -s init stop
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        )
add_test(NAME case4
        COMMAND  ${VALGRIND_PREFIX} erl -noshell -s run case4 -s init stop
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        )
add_test(NAME case5
        COMMAND  ${VALGRIND_PREFIX} erl -noshell -s run case5 -s init stop
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        )
#set_tests_properties(case1 PROPERTIES PASS_REGULAR_EXPRESSION )
